#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Author: leuelcey (wyd)
# E-mail: leuelcey@163.com
# Creation date: 2021.3
# Modified date: 2021.4

from math import floor, ceil, pi as PI
from functools import lru_cache
from basicdata import BasicData
from tools import (sun_alng2time, deltaT, ms_alng2time, ms_alng_Dvalue, sun_alng, base_Juday,
                        Juday2Gregory, Juday2timestring, J2000, Pi2, Climoon)
abd = BasicData()

# 用于定义变量，当参数为`day`时，作为日信息变量
# by leuelcey (wyd)
class Vardays(object):
    """
    日对象。
    实例化：d = Vardays([var_type])，参数为`day`时作为日对象；为空时作为其他变量对象。
    """
    __slots__ = ('__dict__', '__weakref__')

    def __init__(self, var_type=''):
        if var_type == 'day':
            self._set_day_vars()

    def _set_day_vars(self):
        # 日的西历信息
        self.Year = 0  # 所在西历年,同lun.y
        self.Month = 0  # 所在西历月,同lun.m
        self.Day = 0  # 日(西历)
        self.Week = 0  # 星期
        self.DayStart = 0  # 2000.0起算儒略日,北京时12:00
        self.DayOrder = 0  # 所在西历月内日序数
        self.DayTotal = 0  # 所在西历月的总天数,同lun.d0
        self.WeekFirst = 0  # 所在月的月首的星期,同lun.w0
        self.WeekOrder = 0  # 在本月中的周序号
        self.WeekTotal = 0  # 本月的总周数
        # 日的农历信息
        self.yue_ming = ''  # 月名称
        self.ri_ming = ''  # 日名称(农历),即'初一,初二等'
        self.Apart_Firstday = 0  # 距农历月首的编移量,0对应初一
        self.Apart_22ndST = 0  # 距冬至的天数
        self.Apart_10thST = 0  # 距夏至的天数
        self.Apart_13thST = 0  # 距立秋的天数
        self.Apart_9thST = 0  # 距芒种的天数
        self.Apart_11thST = 0  # 距小暑的天数
        self.Big_or_Small = 0  # 月大小
        self.Leap_Month = ''  # 闰状况(值为'闰'或空串)
        self.Next_Month = ''  # 下个月名称,判断除夕时要用到
        self.jieqi_ming = ''  # 当日节气
        # 日的农历纪年、月、日、时及星座
        self.ji_nian = 0  # 农历纪年(10进制,1984年起算,分界点可以是立春也可以是春节,在程序中选择一个)
        self.ganzhi_nian = ''  # 干支纪年
        self.nian0 = 0
        self.nian1 = 0  # 农历年份
        self.nian3 = ''
        self.nian4 = 0
        self.ji_yue = 0  # 纪月处理,1998年12月7日(大雪)开始连续进行节气计数,0为甲子
        self.ganzhi_yue = ''  # 干支纪月
        self.ganzhi_ri = ''  # 纪日
        self.ganzhi_shi = ''  # 纪时
        self.Zodiac = ''  # 星座
        # 日的回历信息
        self.Moslem_Year = 0  # 年(回历)
        self.Moslem_Month = 0  # 月(回历)
        self.Moslem_Day = 0  # 日(回历)
        # 日的其它信息
        self.Moon_Phase = ''  # 月相名称
        self.MPh_Juday = 0  # 月相时刻(儒略日)
        self.MPh_Times = ''  # 月相时间串
        self.Solar_Term = ''  # 节气名称
        self.ST_Juday = 0  # 节气时刻(儒略日)
        self.ST_Times = ''  # 节气时间串
        self.dtpq = ''  # ???
        self.Main_Holiday = ''  # 主节假日
        self.Minor_Holiday = ''  # 次要节假日
        self.Habit_Holiday = ''  # 传统节假日
        self.Day_Off = 0  # 是否放假


# 用于月份变量，参数为`True`时定义日变量
# by leuelcey (wyd)
class Varmonth(object):
    """
    月对象。
    实例化：m = Varmonth([has_days])，参数为`True`时直接实例化日对象(Vardays)；为`False`时不实例化日对象。
    """
    __slots__ = ('__dict__', '__weakref__')

    def __init__(self, has_days=False):
        self.Year = 0  # 西历年份
        self.Month = 0  # 西历月分
        self.WeekFirst = 0  # 本月第一天的星期
        self.DayStart = 0  # 月首的J2000.0起算的儒略日数
        self.DayTotal = 0  # 本月的天数
        self.nian = 0  # 农历年份
        self.yue = ''  # 农历月名
        self.ganzhi_nian = ''  # 该年的干支纪年
        self.sheng_xiao = ''  # 该年的生肖
        self.Reign_Title = ''  # 年号
        if has_days:
            self.Daily = [Vardays('day') for x in range(31)]  # 实例化日对象，按31天设置。
        else:
            self.Daily = None


# 西历(即格里高里历、公历)相关
# 算法及源代码：许剑伟（莆田十中）
# Python 代码：leuelcey (wyd)
class Gregory(object):
    '''
    西历对象。
    计算根据西元制定的节假日、纪念日、特定日以及回历信息。
    '''
    __slots__ = ('sholiday', 'iholiday')

    def __init__(self):
        super(Gregory, self).__init__()
        self.sholiday = abd.get_constant(section='Specialday', varname='Specific')
        self._get_holiday()

    def _get_holiday(self):
        sftv = abd.get_constant(section='Specialday', varname='Important')
        sftvs = sftv.split('|')
        self.iholiday = list(map(lambda s: s.split(','), sftvs))

    # u = Varmonth(True)
    @lru_cache
    def get_dayinfo(self, u):
        m0 = ('0' if u.Month < 10 else '') + str(u.Month)
        d0 = ('0' if u.Day < 10 else '') + str(u.Day)

        if u.Week == 0 or u.Week == 6:
            u.Day_Off = u.Day_Off or 1
        
        for _, s in enumerate(self.iholiday[u.Month - 1]):
            if s[:2] != d0:
                continue

            tp = s[2]
            if '-' in s:
                if u.Year < int(s[3:7]) or u.Year > int(s[8:12]):
                    continue
                s = s[12:]
            else:
                if u.Year < 1850:
                    continue
                s = s[3:]

            if tp == '#':
                u.Main_Holiday = u.Main_Holiday + (';' if u.Main_Holiday and s else '') + s
                u.Day_Off = u.Day_Off or 1
            if tp == 'I':
                u.Minor_Holiday = u.Minor_Holiday + (';' if u.Minor_Holiday and s else '') + s
            if tp == '.':
                u.Habit_Holiday = u.Habit_Holiday + (';' if u.Habit_Holiday and s else '') + s

        w = u.WeekOrder
        if u.Week >= u.WeekFirst:
            w = w + 1
        w2 = w
        if u.WeekOrder == u.WeekTotal - 1:
            w2 = 5
        ws = m0 + str(w) + str(u.Week)
        ws2 = m0 + str(w2) + str(u.Week)
        for _, s in enumerate(self.sholiday):
            if s[:4] != ws and s[:4] != ws2:
                continue
            tp = s[4]
            s = s[5:]
            if tp == '#':
                u.Main_Holiday = u.Main_Holiday + (';' if u.Main_Holiday and s else '') + s
                u.Day_Off = u.Day_Off or 1
            if tp == 'I':
                u.Minor_Holiday = u.Minor_Holiday + (';' if u.Minor_Holiday and s else '') + s
            if tp == '.':
                u.Habit_Holiday = u.Habit_Holiday + (';' if u.Habit_Holiday and s else '') + s

    # r = Vardays('day')
    @lru_cache
    def get_moslems(self, d0, r):
        '''
        计算回历信息。
        :param d0: 起始日数
        :param r: 日对象
        :return: 设置日对象中的回历年、月、日
        '''
        d = d0 + 503105
        z = floor(d / 10631)
        d = d - (z * 10631)
        y = floor((d + 0.5) / 354.366)
        d = d - floor(y * 354.366 + 0.5)
        m = floor((d + 0.11) / 29.51)
        d = d - floor(m * 29.5 + 0.5)
        r.Moslem_Year = z * 30 + y + 1
        r.Moslem_Month = m + 1
        r.Moslem_Day = d + 1


# 农历(包含太阳历与阴历)相关
# 算法及源代码：许剑伟（莆田十中）
# Python 代码：leuelcey (wyd)
class Nongli(object):
    '''
    农历对象。
    包含干支纪年、月、日，朝代，节气，农历年、月、日、节假日，生肖，星座等信息。
    '''
    __slots__ = ('shuzi', 'gan', 'zhi', 'xiao', 'Zodiac', 'yxmc', 'jqmc', 'ymc', 'rmc', 'Dynasty', 'jiari')

    def __init__(self):
        super(Nongli, self).__init__()
        self.shuzi = abd.get_constant(section='calendar', varname='shuzi')
        self.gan = abd.get_constant(section='bazi', varname='gan')
        self.zhi = abd.get_constant(section='bazi', varname='zhi')
        self.xiao = abd.get_constant(section='bazi', varname='xiao')
        self.Zodiac = abd.get_constant(section='calendar', varname='Zodiac')
        self.yxmc = abd.get_constant(section='calendar', varname='xiang')
        self.jqmc = abd.get_constant(section='calendar', varname='jieqi')
        self.ymc = abd.get_constant(section='calendar', varname='yming')
        self.rmc = abd.get_constant(section='calendar', varname='rming')
        self.Dynasty = abd.get_constant(section='Annles', varname='Dynasty')
        self.jiari = abd.get_constant(section='Specialday', varname='Festival')

    def hq_niandai_shu(self, niandai=0):
        """
        取年代的中文表示。
        :param niandai: 年代数
        :return: 中文年代
        """
        if not isinstance(niandai, (int, float)) or niandai < 0 or niandai > 99999:
            return ''

        if not isinstance(niandai, int):
            niandai = ceil(niandai)
        shiwei = {'10': '十', '20': '廿', '30': '卅'}
        danshu = {'d1': '元', '0': '', '1': '一', '2': '二', '3': '三', '4': '四', '5': '五', '6': '六', '7': '七', '8': '八', '9': '九'}

        def _hq_shige_shu(h2num=''):
            if not h2num:
                return ''
            if h2num[0] in ('1', '2', '3') and h2num[1] != '0':
                sw = shiwei[h2num[0] + '0']
                return sw + danshu[h2num[1]]
            return danshu[h2num[0]] + (shiwei['10'] if h2num[0] != '0' else '') + danshu[h2num[1]]

        if niandai < 10:
            gw = 'd1' if niandai == 1 else str(niandai)
            return danshu[gw]
        if niandai < 99:
            return _hq_shige_shu(str(niandai))

        danwei = ('万', '千', '百')
        h2 = _hq_shige_shu(str(niandai)[-2:])
        ns = str(niandai)[:-2]
        nd = ''
        for k, s in enumerate(ns):
            w = (k - (len(ns) - 1)) + 2
            nd = nd + danshu[s] + (danwei[w] if danshu[s] else '')
        return nd + h2

    def hq_nianhao(self, y):
        """
        取年号。
        用法：nh = hq_nianhao(<西元年>)
        :param y: 西元年
        :return: 中文年号
        """
        s = ''
        for _, chaodai in enumerate(self.Dynasty):
            if y < chaodai[0] or y >= chaodai[0] + chaodai[1]:
                continue
            c = chaodai[6] + self.hq_niandai_shu(y - chaodai[0] + 1 + chaodai[2]) + '年'
            s = s + (';' if s.strip() else '') + '【' + chaodai[3] + '】' + chaodai[4] + ' ' + chaodai[5] + ' ' + c
        return s

    # u = Vardays('day')
    @lru_cache
    def hq_ri_xinxi(self, u):
        """
        农历日信息。
        :param u: 日对象
        :return: 设置日对象的相应属性值
        """
        if u.Leap_Month != '闰':  # 非闰月时的传统节假日
            if u.yue_ming != '腊' and u.yue_ming in self.jiari and u.ri_ming in self.jiari[u.yue_ming]:
                jr = self.jiari[u.yue_ming][u.ri_ming]
                u.Main_Holiday = u.Main_Holiday + (';' if u.Main_Holiday and jr['zming'] else '') + jr['zming']
                u.Minor_Holiday = u.Minor_Holiday + (';' if u.Minor_Holiday and jr['bming'] else '') + jr['bming']
                u.Habit_Holiday = u.Habit_Holiday + (';' if u.Habit_Holiday and jr['cming'] else '') + jr['cming']
                u.Day_Off = u.Day_Off or jr['jia']
            if u.yue_ming == '腊' and u.ri_ming == '初八' and u.yue_ming in self.jiari and u.ri_ming in self.jiari[u.yue_ming]:
                jr = self.jiari[u.yue_ming][u.ri_ming]
                u.Main_Holiday = u.Main_Holiday + (';' if u.Main_Holiday and jr['zming'] else '') + jr['zming']
                u.Minor_Holiday = u.Minor_Holiday + (';' if u.Minor_Holiday and jr['bming'] else '') + jr['bming']
                u.Habit_Holiday = u.Habit_Holiday + (';' if u.Habit_Holiday and jr['cming'] else '') + jr['cming']
                u.Day_Off = u.Day_Off or jr['jia']

        if u.Next_Month == '正' and u.yue_ming == '腊':  # 下月为正月时，判断大月除夕还是小月除夕以及小年
            if u.ri_ming == '三十' and u.Big_or_Small == 30:
                rm = '三十'
            elif u.ri_ming == '廿九' and u.Big_or_Small == 29:
                rm = '廿九'
            elif u.ri_ming == '廿三':
                rm = '廿三'
            else:
                rm = ''

            if u.yue_ming in self.jiari and rm in self.jiari[u.yue_ming]:
                jr = self.jiari[u.yue_ming][rm]
                u.Main_Holiday = u.Main_Holiday + (';' if u.Main_Holiday and jr['zming'] else '') + jr['zming']
                u.Minor_Holiday = u.Minor_Holiday + (';' if u.Minor_Holiday and jr['bming'] else '') + jr['bming']
                u.Habit_Holiday = u.Habit_Holiday + (';' if u.Habit_Holiday and jr['cming'] else '') + jr['cming']
                u.Day_Off = u.Day_Off or jr['jia']

        if u.jieqi_ming:  # 当日有节气存在时
            if '无' in self.jiari and u.jieqi_ming in self.jiari['无']:
                jr = self.jiari['无'][u.jieqi_ming]
                u.Main_Holiday = u.Main_Holiday + (';' if u.Main_Holiday and jr['zming'] else '') + jr['zming']
                u.Minor_Holiday = u.Minor_Holiday + (';' if u.Minor_Holiday and jr['bming'] else '') + jr['bming']
                u.Habit_Holiday = u.Habit_Holiday + (';' if u.Habit_Holiday and jr['cming'] else '') + jr['cming']
                u.Day_Off = u.Day_Off or jr['jia']
            else:
                u.Minor_Holiday = u.Minor_Holiday + (';' if u.Minor_Holiday and u.jieqi_ming else '') + u.jieqi_ming

        w, w2 = '', ''
        if 0 <= u.Apart_22ndST < 81:
            w = self.shuzi[floor(u.Apart_22ndST / 9) + 1]
            if u.Apart_22ndST % 9 == 0:
                u.Minor_Holiday = u.Minor_Holiday + (';' if u.Minor_Holiday else '') + '「' + w + '九」'
            else:
                u.Habit_Holiday = u.Habit_Holiday + (';' if u.Habit_Holiday else '') + w + '九 第 ' + str(u.Apart_22ndST % 9 + 1) + ' 天'

        w = u.ganzhi_ri[0]
        w2 = u.ganzhi_ri[1]
        if 20 <= u.Apart_10thST < 30 and w == '庚':
            u.Minor_Holiday = u.Minor_Holiday + (';' if u.Minor_Holiday else '') + '初伏'
        if 30 <= u.Apart_10thST < 40 and w == '庚':
            u.Minor_Holiday = u.Minor_Holiday + (';' if u.Minor_Holiday else '') + '中伏'
        if 0 <= u.Apart_13thST < 10 and w == '庚':
            u.Minor_Holiday = u.Minor_Holiday + (';' if u.Minor_Holiday else '') + '末伏'
        if 0 <= u.Apart_9thST < 10 and w == '丙':
            u.Minor_Holiday = u.Minor_Holiday + (';' if u.Minor_Holiday else '') + '入梅'
        if 0 <= u.Apart_11thST < 12 and w2 == '未':
            u.Minor_Holiday = u.Minor_Holiday + (';' if u.Minor_Holiday else '') + '出梅'

    # -------------------- 以下为气朔计算 --------------------
    @lru_cache
    def qi_accurate(self, W):
        t = sun_alng2time(W) * 36525
        return t - deltaT(t) + 8 / 24

    @lru_cache
    def so_accurate(self, W):
        t = ms_alng2time(W) * 36525
        return t - deltaT(t) + 8 / 24

    @lru_cache
    def qi_accurate2(self, jd):
        d = PI / 12
        w = floor((jd + 293) / 365.2422 * 24) * d
        a = self.qi_accurate(w)
        if a - jd > 5:
            return self.qi_accurate(w - d)
        if a - jd < -5:
            return self.qi_accurate(w + d)
        return a

    @lru_cache
    def so_accurate2(self, jd):
        return self.so_accurate(floor((jd + 8) / 29.5306) * PI * 2)


# 实际历法计算，调用以上各类及相关函数
class Annles(object):
    """
    历法类，调用以上类及相关方法和函数。
    """
    __slots__ = ('gcalen', 'ncalen', 'qishuo', 'monthly')

    def __init__(self):
        super(Annles, self).__init__()
        self.monthly = None  # 月对象
        self.gcalen = Gregory()  # 西历对象
        self.ncalen = Nongli()  # 农历对象
        self.qishuo = Climoon()  # 气朔计算对象

    # by leuelcey (wyd)
    def cleanup(self, oprtype=''):
        if self.monthly:
            if self.monthly.Daily:
                self.monthly.Daily = None
            self.monthly = None
        # 只清除月和日对象
        if oprtype == 'clear':
            return
        # 初始化月和日对象
        self.monthly = Varmonth(True)

    # 算法及源代码：许剑伟（莆田十中）
    # Python 代码：leuelcey (wyd)
    @lru_cache
    def month_calendar(self, By, Bm):
        """
        月历计算主程序。
        用法：Annles.month_calendar(<西元年>, <西元月>)
        :param By: 西元年
        :param Bm: 西元月
        :return: 设置月和日对象的各项属性值
        """
        self.cleanup()

        xY = By
        xM = Bm
        Bd0 = floor(base_Juday(xY, xM) - J2000)
        xM = xM + 1
        if xM > 12:
            xY = xY + 1
            xM = 1
        Bdn = floor(base_Juday(xY, xM) - J2000 - Bd0)

        self.monthly.WeekFirst = (Bd0 + J2000 + 1 + 7000000) % 7
        self.monthly.Year = By
        self.monthly.Month = Bm
        self.monthly.DayStart = Bd0
        self.monthly.DayTotal = Bdn

        jz = By - 1984 + 12000
        self.monthly.nian = By
        self.monthly.ganzhi_nian = self.ncalen.gan[jz % 10] + self.ncalen.zhi[jz % 12]
        self.monthly.sheng_xiao = self.ncalen.xiao[jz % 12]
        self.monthly.Reign_Title = self.ncalen.hq_nianhao(By)

        D, w, j = 0, 0, 0
        ob, ob2 = None, None
        for i in range(Bdn):
            ob = self.monthly.Daily[i]

            ob.DayStart = Bd0 + i
            ob.DayOrder = i
            ob.Year = By
            ob.Month = Bm
            ob.DayTotal = Bdn
            ob.WeekFirst = self.monthly.WeekFirst
            ob.Week = (self.monthly.WeekFirst + i) % 7
            ob.WeekOrder = floor((self.monthly.WeekFirst + i) / 7)
            ob.WeekTotal = floor((self.monthly.WeekFirst + Bdn - 1) / 7) + 1
            _, _, ob.Day, _, _, _ = Juday2Gregory(ob.DayStart + J2000)

            if not self.qishuo.ZQ:
                self.qishuo.yue_Sort(ob.DayStart)
            else:
                if ob.DayStart < self.qishuo.ZQ[0] or ob.DayStart >= self.qishuo.ZQ[24]:
                    self.qishuo.yue_Sort(ob.DayStart)

            mk = floor((ob.DayStart - self.qishuo.HS[0]) / 30)
            if mk < 13 and self.qishuo.HS[mk + 1] <= ob.DayStart:
                mk = mk + 1

            ob.Apart_Firstday = ob.DayStart - self.qishuo.HS[mk]
            ob.ri_ming = self.ncalen.rmc[ob.Apart_Firstday]
            ob.Apart_22ndST = ob.DayStart - self.qishuo.ZQ[0]
            ob.Apart_10thST = ob.DayStart - self.qishuo.ZQ[12]
            ob.Apart_13thST = ob.DayStart - self.qishuo.ZQ[15]
            ob.Apart_9thST = ob.DayStart - self.qishuo.ZQ[11]
            ob.Apart_11thST = ob.DayStart - self.qishuo.ZQ[13]
            if ob.DayStart == self.qishuo.HS[mk] or ob.DayStart == Bd0:
                ob.yue_ming = self.qishuo.ym[mk]
                ob.Big_or_Small = self.qishuo.dx[mk]
                ob.Leap_Month = '闰' if (self.qishuo.leap != 0 and self.qishuo.leap == mk) else ''
                ob.Next_Month = self.qishuo.ym[mk + 1] if (mk < 13) else "未知"
            else:
                ob2 = self.monthly.Daily[i - 1]
                ob.yue_ming = ob2.yue_ming
                ob.Big_or_Small = ob2.Big_or_Small
                ob.Leap_Month = ob2.Leap_Month
                ob.Next_Month = ob2.Next_Month

            qk = floor((ob.DayStart - self.qishuo.ZQ[0] - 7) / 15.2184)
            if qk < 23 and ob.DayStart >= self.qishuo.ZQ[qk + 1]:
                qk = qk + 1
            if ob.DayStart == self.qishuo.ZQ[qk]:
                ob.jieqi_ming = self.ncalen.jqmc[qk]
            else:
                ob.jieqi_ming = ''
            # ??? ob.dtpq = Dtlpq(qk) ???

            D = self.qishuo.ZQ[3] + (-365 if ob.DayStart < self.qishuo.ZQ[3] else 0) + 365.25 * 16 - 35
            ob.ji_nian = floor(D / 365.2422 + 0.5)
            D = self.qishuo.HS[2]
            for j in range(14):
                if (self.qishuo.ym[j] != '正' or self.qishuo.leap == j) and j != 0:
                    continue
                D = self.qishuo.HS[j]
                if ob.DayStart < D:
                    D = D - 365
                    break
            
            D = D + 5810
            ob.nian0 = floor(D / 365.2422 + 0.5)
            D = ob.ji_nian + 12000
            ob.ganzhi_nian = self.ncalen.gan[D % 10] + self.ncalen.zhi[D % 12]

            # by leuelcey (wyd)
            if i == 0:
                if D != jz:
                    jz = 60 if jz == 0 else jz
                    self.monthly.nian = (By - 1) if jz > D else (By + 1)
                    self.monthly.ganzhi_nian = self.ncalen.gan[D % 10] + self.ncalen.zhi[D % 12]
                    self.monthly.sheng_xiao = self.ncalen.xiao[D % 12]
                self.monthly.yue = ob.Leap_Month + ob.yue_ming + '月' + ('大' if ob.Big_or_Small == 30 else '小')

            ob.nian1 = self.monthly.nian
            D = ob.nian0 + 12000
            ob.nian3 = self.ncalen.gan[D % 10] + self.ncalen.zhi[D % 12]
            ob.nian4 = ob.nian0 + 1984 + 2698

            mk = floor((ob.DayStart - self.qishuo.ZQ[0]) / 30.43685)
            if mk < 12 and ob.DayStart >= self.qishuo.ZQ[2 * mk + 1]:
                mk = mk + 1
            D = mk + floor((self.qishuo.ZQ[12] + 390) / 365.2422) * 12 + 900000
            ob.ji_yue = D % 12
            ob.ganzhi_yue = self.ncalen.gan[D % 10] + self.ncalen.zhi[D % 12]
            D = ob.DayStart - 6 + 9000000
            ob.ganzhi_ri = self.ncalen.gan[D % 10] + self.ncalen.zhi[D % 12]
            mk = floor((ob.DayStart - self.qishuo.ZQ[0] - 15) / 30.43685)
            if mk < 11 and ob.DayStart >= self.qishuo.ZQ[2 * mk + 2]:
                mk = mk + 1
            ob.Zodiac = self.ncalen.Zodiac[(mk + 12) % 12] + '座'
            self.gcalen.get_moslems(ob.DayStart, ob)
            self.gcalen.get_dayinfo(ob)
            self.ncalen.hq_ri_xinxi(ob)


        d, xn, D = 0, 0, 0
        jd2 = Bd0 + deltaT(Bd0) - 8 / 24
        w = ms_alng_Dvalue(jd2 / 36525, 10, 3)
        w = floor((w - 0.78) / PI * 2) * PI / 2
        while D + 5 < Bd0 + Bdn:
            d = self.ncalen.so_accurate(w)
            D = floor(d + 0.5)
            xn = floor(w / Pi2 * 4 + 4000000.01) % 4
            w = w + (Pi2 / 4)
            if D >= Bd0 + Bdn:
                break
            if D < Bd0:
                continue
            ob = self.monthly.Daily[D - Bd0]
            ob.Moon_Phase = self.ncalen.yxmc[xn]
            ob.MPh_Juday = d
            ob.MPh_Times = Juday2timestring(d)

        w = sun_alng(jd2 / 36525, 3)
        w = floor((w - 0.13) / Pi2 * 24) * Pi2 / 24
        D = 0
        while D + 12 < Bd0 + Bdn:
            d = self.ncalen.qi_accurate(w)
            D = floor(d + 0.5)
            xn = floor(w / Pi2 * 24 + 24000006.01) % 24
            w = w + (Pi2 / 24)
            if D >= Bd0 + Bdn:
                break
            if D < Bd0:
                continue
            ob = self.monthly.Daily[D - Bd0]
            ob.Solar_Term = self.ncalen.jqmc[xn]
            ob.ST_Juday = d
            ob.ST_Times = Juday2timestring(d)

    # by leuelcey (wyd)
    def datas_convert(self, *, HasMonth=True, BeginDay=0, Days=0):
        """
        月历数据转换。可以提取1天或几天的数据。
        用法：dc = Annles.datas_convert([HasMonth][BeginDay][Days])
        :param HasMonth: 是否转换月信息，默认为True
        :param BeginDay: 开始日，默认为0
        :param Days: 转换天数，默认为0
        :return: 转换后的字典数据
        """
        if not self.monthly:
            return {}
        if not HasMonth and (BeginDay <= 0 or Days <= 0):
            return {}
        if BeginDay > self.monthly.DayTotal or (BeginDay == self.monthly.DayTotal and Days != 1):
            return {}

        datas = {}
        if BeginDay <= 0 or Days <= 0:
            Enday = 0
        else:
            Enday = (BeginDay - 1 + Days) if (BeginDay - 1 + Days <= self.monthly.DayTotal) else self.monthly.DayTotal

        for km, vm in self.monthly.__dict__.items():
            if km != 'Daily':
                if HasMonth:
                    datas[km] = vm
            elif Enday > 0:
                days = [None for x in range(BeginDay - 1, Enday)]
                j = 0
                for i in range(BeginDay - 1, Enday):
                    days[j] = vm[i].__dict__
                    j = j +1
                datas[km] = days
        return datas

    # by leuelcey (wyd)
    def show_calendar(self):
        """
        显示月历信息。
        用法：Annles.show_calendar()
        :return: 无
        """
        if not self.monthly:
            return

        lstart = '        '
        week = self.monthly.WeekFirst
        snian = hq_nl_nian(self.monthly.nian)
        title = ['' for x in range(10)]

        s = f'农历 {snian} {self.monthly.ganzhi_nian}【{self.monthly.sheng_xiao}】年 {self.monthly.yue}'
        title[0] = s.center(47 - len(s) + 3)
        title[6] = f'西元 {self.monthly.Year} 年 {self.monthly.Month} 月'.center(43)
        title[7] = '-'
        title[8] = '    日    一    二    三    四    五    六'
        title[9] = '  ==========================================='

        nianhao = self.monthly.Reign_Title.split(';')
        for kn, nh in enumerate(nianhao):
            title[kn + 1] = f'{nh}'.center(47 - len(nh) + 2)

        print()
        for _, t in enumerate(title):
            if t and t != '-':
                print(lstart + t)
            elif t == '-':
                print()
        
        impday = {}
        for i in range(self.monthly.DayTotal):
            impday[i + 1] = ''
            jjrday = ''
            yuefen = '(' + self.monthly.Daily[i].yue_ming + '月' + self.monthly.Daily[i].ri_ming + ') '
            if self.monthly.Daily[i].Main_Holiday:
                jjrday = jjrday + ('；' if jjrday else '') + (yuefen if '月' not in jjrday else '') + self.monthly.Daily[i].Main_Holiday
            if '龙抬头' in self.monthly.Daily[i].Minor_Holiday or '七夕' in self.monthly.Daily[i].Minor_Holiday or '中元节' in self.monthly.Daily[i].Minor_Holiday or '重阳节' in self.monthly.Daily[i].Minor_Holiday or '腊八节' in self.monthly.Daily[i].Minor_Holiday:
                jjrday = jjrday + ('；' if jjrday else '') + (yuefen if '月' not in jjrday else '') + self.monthly.Daily[i].Minor_Holiday
            if self.monthly.Daily[i].jieqi_ming in ('立春', '夏至', '立秋', '冬至'):
                jjrday = jjrday + ('；' if jjrday else '') + (yuefen if '月' not in jjrday else '') + self.monthly.Daily[i].jieqi_ming
            impday[i + 1] = jjrday

            if i == 0:
                print(lstart + '  ', end=' ')
                print('  ' * 3 * week, end=' ')
            px = (' ' if i+1 < 10 else '') + str(i + 1) + '   '
            print(px, end=' ')
            if (week+i+1) % 7 == 0:
                print()
                print(lstart + '   ', end=' ')
        print('\n')

        if impday:
            for k, v in impday.items():
                if v:
                    print(lstart + str(k) + ' 日：' + v)
            print()

# 算法及源代码：许剑伟（莆田十中）
# Python 代码及代码贡献：leuelcey (wyd)
@lru_cache
def hq_24jieqi(year=0, begin=1, total=14):
    """
    取24节气信息。
    用法：minfo, jinfo = hq_24jieqi([西元年][开始月][总计月])
    :param year: 西元年，默认为0
    :param begin: 开始月，默认为1
    :param total: 总计月，默认为14(为覆盖全年所有节气，前后各多取1个月)
    :return: (年份, 月份名, 闰月状态, 闰月索引), [(节气名, 儒略日数)...]
    """
    if begin <= 0 or begin > 14 or total < 1 or total > 14:
        return None
    begin = 13 if (begin - 1 > 13) else (begin - 1)
    total = 14 if (begin + total > 14) else (begin + total)

    nongli = Nongli()
    qishuo = Climoon()

    qishuo.yue_Sort(floor((year - 2000) * 365.2422 + 180))
    jieqi = []
    yfmc = ''
    ryzt = ''
    rysy = -1

    for i in range(begin, total):
        if qishuo.HS[i + 1] > qishuo.ZQ[24]:
            break

        yfmc = yfmc + (',' if (i != 0 and yfmc != '') else '') + qishuo.ym[i]
        if qishuo.leap == i:
            ryzt = '闰'
            rysy = i - begin

        qi = 0
        for j in range(-2, 24):
            if j == -2:
                qi = qishuo.pe['pe2']
            if j == -1:
                qi = qishuo.pe['pe1']
            if j >= 0:
                qi = qishuo.ZQ[j]

            if qi < qishuo.HS[i] or qi >= qishuo.HS[i + 1]:
                continue

            jq = nongli.jqmc[(j + 24) % 24]
            v2 = nongli.qi_accurate2(qi)
            jd = v2 + J2000
            jieqi = jieqi + [(jq, jd)]

    return (year, yfmc, ryzt, rysy), jieqi

# 取年的农历表示
# by leuelcey (wyd)
def hq_nl_nian(year=0):
    """
    取年的农历表示。
    用法：nlnian = hq_nl_nian([年])
    :param year: 年份数
    :return: 农历年中文字符串
    """
    cn_digit = ('〇', '一', '二', '三', '四', '五', '六', '七', '八', '九')
    nl_nian = [cn_digit[int(s)] for _, s in enumerate(str(year))]
    return ''.join(nl_nian)

# 取星期
# by leueley (wyd)
def get_Week(week=0):
    """
    取星期。
    用法：wk = get_Week([星期值])
    :param week: 星期值，0为星期日
    :return: 星期名
    """
    sweek = ('日', '一', '二', '三', '四', '五', '六')
    return '星期' + sweek[week]

